using System;
using System.Text;
using System.IO;
using System.Reflection;
using System.Runtime;
using System.Configuration;
using System.Collections;
using System.Collections.Specialized;

namespace DotNetScriptEngine
{
	/// <summary>
	/// Summary description for Class1.
	/// </summary>
	public class Engine : ATrace
	{	
		private const int NoOrErrorRetValue = -1;	


		public int RunText(string Scripts)
		{
			int result = NoOrErrorRetValue;
			AssemblyGenerator assGen = null;
			
			try
			{
				if (Scripts != null && Scripts != "")
				{
					try
					{	
						//Create a new AssemblyGenerator class, based on the csml text
						assGen = new AssemblyGenerator(Scripts);
						if (assGen != null)
						{
							assGen.Trace += new TraceEventHandler(TraceException);

							//Create an assembly in memmory
							Assembly assembly = assGen.CreateAssembly();

							//Use reflection to call the Main function in the assembly
							result = RunScript(assembly, assGen.script.EntryPoint);					

							if (assGen.script.WaitForUserAction)
							{
								OnTrace ("Waiting for user action.", null);
								Console.ReadLine();
							}

							//I know i dont have too, but it makes me feel better
							assembly = null;
						}
						//if assGen was not created or if user wants console to remain open, then ReadLine
						if (assGen == null)
							Console.ReadLine();
					}
					catch (Exception ex)
					{
						OnTrace ("Error: An exception occurred while compiling the script", ex);
					}
				}							
			}
			catch (Exception ex)
			{				
				OnTrace ("Error: An exception occurred while running the script file", ex);				
			}

			return result;
		}

		public int RunFile(string File)
		{
			//load the csml text from the file
			string fileContents = GetFileContent(File);

			if (fileContents.Length > 0)
				return RunText(fileContents);
			else
				return NoOrErrorRetValue;
		}

		private string GetFileContent(string FileName)
		{
			if (FileName != "" && File.Exists(FileName))
			{
				StreamReader reader = null;
				string fileContents = string.Empty;

				try
				{
					reader = new StreamReader(FileName, Encoding.Default);
					fileContents = reader.ReadToEnd().Trim();
				}
				catch (Exception ex)
				{
					OnTrace ("Error: An exception occurred while reading the code from the script file", ex);
				}
				finally
				{
					if (reader != null)
						reader.Close();
				}

				if (fileContents.Length == 0)
				{
					OnTrace ("Error: The dnml Script file is empty\r\n", null);
				}

				return fileContents;
			}
			else
			{
				OnTrace ("Error: The dnml Script file does not exist\r\n", null);
				return "";
			}
		}
	
		private int RunScript(Assembly assembly, string entryPoint)
		{
			try
			{
				//cant call the entry method if the assembly is null
				if (assembly != null)
				{
					//Use reflection to call the static Main function
					Module[] mods = assembly.GetModules(false);
					Type[] types = mods[0].GetTypes();

					//loop through each class that was defined and look for the first occurrance of the entry point method
					foreach (Type type in types)
					{
						MethodInfo mi = type.GetMethod(entryPoint, BindingFlags.Public | BindingFlags.Static);
						if (mi != null)
						{
							if (mi.ReturnType.Name != "Int32")
							{
								//if the entry point method doesnt return an Int32, then return the error constant
								mi.Invoke(null, null);
								return NoOrErrorRetValue;
							}
							else
							{
								//if the entry point method does return in Int32, then capture it and return it
								return (int)mi.Invoke(null, null);
							}
						}
					}

					//if it got here, then there was no entry point method defined.  Tell user about it
					OnTrace ("Dot Net Script Engine could not find the public static " + entryPoint + " entry function to execute", null);
				}
			}
			catch (Exception ex)
			{
				OnTrace ("Error: An exception occurred while executing the script", ex);			
			}

			return NoOrErrorRetValue;
		}

		private void TraceException(object sender, TraceEventArgs e)
		{
			OnTrace (e.Trace, e.Ex);
		}
	}
}
